package com.tanhua.dubbo.server.api;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.PageUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.dubbo.config.annotation.Service;
import com.tanhua.dubbo.server.enums.CommentType;
import com.tanhua.dubbo.server.enums.IdType;
import com.tanhua.dubbo.server.pojo.*;
import com.tanhua.dubbo.server.service.IdService;
import com.tanhua.dubbo.server.service.TimeLineService;
import com.tanhua.dubbo.server.vo.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.bson.types.ObjectId;
import org.omg.CORBA.PRIVATE_MEMBER;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.ArrayList;
import java.util.List;

@Service(version = "1.0.0")
@Slf4j
public class QuanZiApiImpl implements QuanZiApi{

    @Autowired
    private MongoTemplate mongoTemplate;

    @Autowired
    private IdService idService;

    @Autowired
    private TimeLineService timeLineService;

    @Autowired
    private RedisTemplate<String,String> redisTemplate;

    //评论数据存储在Redis中key的前缀
    private static final String COMMENT_REDIS_KEY_PREFIX="QUANZI_COMMENT_";
    //用户是否点赞的前缀
    private static final String COMMENT_USER_LIKE_REDIS_KEY_PREFIX="USER_LIKE_";
    //用户是否喜欢的前缀
    private static final String COMMENT_USER_LOVE_REDIS_KEY_PREFIX="USER_LOVE_";

    @Autowired
    private VideoApi videoApi;

    /**在自己的好友时间线表中查询符合条件的数据(MongoDB)
     *
     * @param userId 用户id
     * @param page 当前页数
     * @param pageSize 每一页查询的数据条数
     * @return
     */
    @Override
    public PageInfo<Publish> queryPublishList(Long userId, Integer page, Integer pageSize) {
        //分析：查询好友的动态,实际上查询时间线表
        //用PageInfo对象封装有页面相关信息，以及publish,就是items
        PageInfo<Publish> pageInfo = new PageInfo<>();
        //设置已知的相关属性
        pageInfo.setPageNum(page);
        pageInfo.setPageSize(pageSize);

        //设置分页的条件
        Pageable pageable = PageRequest.of(page-1,pageSize,
                Sort.by(Sort.Order.desc("date")));
        //构造查询构造器
        Query query=new Query().with(pageable);

        //通过MongoDB查询相关符合条件的数据，并用集合进行封装
        List<TimeLine> timeLineList = mongoTemplate.find(query, TimeLine.class, "quanzi_time_line_" + userId);
        if(CollUtil.isEmpty(timeLineList)){
            //没有查询到数据
            return pageInfo;
        }

        //获取时间线表中的发布publishId的列表
        List<Object> ids = CollUtil.getFieldValues(timeLineList, "publishId");

        //构造在publish表中的查询条件
        Query queryPublish=Query.query(Criteria.where("id").in(ids))
                .with(Sort.by(Sort.Order.desc("created")));

        //在MongoDB中的publish表中查询发布的动态信息，并用集合进行封装
        List<Publish> publishList=mongoTemplate.find(queryPublish,Publish.class);

        //PageInfo对象封装publishList，即为records
        pageInfo.setRecords(publishList);

        return pageInfo;
    }

    /**
     * 发布动态(写入publish表，写入相册表，写入时间线表)
     * @param publish
     * @return
     */
    @Override
    public String savePublish(Publish publish) {
        //对publish对象校验，文字和发布者id不能为空
        if(!ObjectUtil.isAllNotEmpty(publish.getText(),publish.getUserId())){
            //发布失败
            return null;
        }

        //设置publish对应quanzi_publish表中的主键
        publish.setId(ObjectId.get());

        try {
            //设置quanzi_publish自增长的pid，MongoDB不具备自增长性质，这里通过redis
            publish.setPid(idService.createId(IdType.PUBLISH));
            publish.setCreated(System.currentTimeMillis());

            //写入到quanzi_publish表中
            mongoTemplate.save(publish);

            //写入相册表（即用户自己发布的动态表，他的publishid与quanzi_publish中的id对应）
            Album album=new Album();
            album.setId(ObjectId.get());
            album.setCreated(System.currentTimeMillis());
            album.setPublishId(publish.getId());

            mongoTemplate.save(album,"quanzi_album_"+publish.getUserId());

            //写入时间线表（异步写入）
            timeLineService.saveTimeLine(publish.getUserId(),publish.getId());
        } catch (Exception e) {
            //TODO 需要做事务的混滚，MongoDB的单节点服务，不支持事务，对于混滚我们暂时不实现了
            log.error("发布动态失败 publish="+publish,e);
        }

        return publish.getId().toHexString();
    }


    /**
     * 查询推荐动态列表
     * @param userId
     * @param page
     * @param pageSize
     * @return
     */
    @Override
    public PageInfo<Publish> queryRecommendPublishList(Long userId, Integer page, Integer pageSize) {
        //创建PageInfo对象。其集合封装了Publish对象
        PageInfo<Publish> pageInfo=new PageInfo<>();
        //给已经知道的属性赋值
        pageInfo.setPageNum(page);
        pageInfo.setPageSize(pageSize);

        //构造查询的key
        String key="QUANZI_PUBLISH_RECOMMEND_"+userId;
        //查询推荐的结果数据
        String  data= redisTemplate.opsForValue().get(key);
        //判断是否为空，如果为空则直接返回
        if (StrUtil.isEmpty(data)){
            return pageInfo;
        }

        //查询到的pid进行分页
        List<String> pids=StrUtil.split(data,',');
        //计算分页
        //[0,10]
        int[] startEnd = PageUtil.transToStartEnd(page - 1, pageSize);
        int startIndex = startEnd[0];//开始
        int endIndex=Math.min(startEnd[1],pids.size());//结束

        //将得到的分页得到的pid添加到集合中
        List<Long> pidLongList=new ArrayList<>();
        for (int i = startIndex; i < endIndex; i++) {
            pidLongList.add(Long.valueOf(pids.get(i)));
        }

        //判断pidLongList是否为空
        if (CollUtil.isEmpty(pidLongList)){
            //没有查询到数据
            return pageInfo;
        }

        //构建查询条件
        Query query=Query.query(Criteria.where("pid").in(pidLongList))
                .with(Sort.by(Sort.Order.desc("created")));
        //根据构造的pid的查询条件去MongoDB的publish表中查询
        List<Publish> publishList=mongoTemplate.find(query,Publish.class);
        //判断publishList是否为空
        if (CollUtil.isEmpty(pidLongList)){
            //没有查询到数据
            return pageInfo;
        }

        //将publishList添加到pageInfo的records中
        pageInfo.setRecords(publishList);
        //返回pageInfo对象
        return pageInfo;
    }


    /**
     * 判断用户是否点赞
     * @param userId
     * @param publishId
     * @return
     */
    @Override
    public Boolean likeComment(Long userId, String publishId) {
        //判断该用户是否已经点赞,如果已经点赞就直接返回
        if (this.queryUserIsLike(userId,publishId)){
            return false;
        }

        //保存Comment数据,就是去MongoDB的评论表添加数据
        Boolean result=this.saveComment(userId,publishId,CommentType.LIKE,null);
        if(!result){
            return false;
        }

        //修改redis中的点赞数以及是否点赞

        //修改点赞数
        //设置redis的key
        String redisKey=this.getCommentRedisKeyPrefix(publishId);
        //设置hash数据的key
        String hashKey=CommentType.LIKE.toString();
        redisTemplate.opsForHash().increment(redisKey,hashKey,1);

        //修改用户是否点赞
        String userHashKey=this.getCommentUserLikeRedisKeyPrefix(userId);
        redisTemplate.opsForHash().put(redisKey,userHashKey,"1");

        return true;
    }

    //查询用户是否点赞
    @Override
    public Boolean queryUserIsLike(Long userId, String publishId){
        //从redis中查询数据(hash类型)

        //设置查询的key
        String redisKey=this.getCommentRedisKeyPrefix(publishId);
        //设置用户是否点赞的hashkey
        String userHashKey=this.getCommentUserLikeRedisKeyPrefix(userId);
        //从redis中获取当前用户点赞状态
        Object data = redisTemplate.opsForHash().get(redisKey, userHashKey);
        //判断data是否为空
        if(ObjectUtil.isNotEmpty(data)){
            //不为空将data数据返回，“1"类型为点赞
            return StrUtil.equals(Convert.toStr(data),"1");
        }

        //redis中可能存在数据丢失的情况，如果没有查到数据再到MongoDB中确认一下
        //构建查询条件,在quanzi_comment查询,从publishId，userId，commentType三者结合查询，结果才能保证准确
        Query query=Query.query(Criteria.where("publishId").is(new ObjectId(publishId))
                .and("userId").is(userId)
                .and("commentType").is(CommentType.LIKE));

        //查询MongoDB,确定是否已经点赞
        long count = mongoTemplate.count(query, Comment.class);
        //判断comment是否为空
        if(count==0){
            return false;
        }

        //如果当前用户已经点赞,再写回redis
        redisTemplate.opsForHash().put(redisKey,userHashKey,"1");

        return true;
    }

    //评论数据存储在Redis中key
    private String getCommentRedisKeyPrefix(String publishId){
        return COMMENT_REDIS_KEY_PREFIX+publishId;
    }

    //用户是否点赞
    private String getCommentUserLikeRedisKeyPrefix(Long userId){
        return COMMENT_USER_LIKE_REDIS_KEY_PREFIX+userId;
    }

    //用户是否喜欢
    private String getCommentUserLoveRedisKey(Long userId){
        return COMMENT_USER_LOVE_REDIS_KEY_PREFIX+userId;
    }

    //往MongoDB的评论表写入数据
    private Boolean saveComment(Long userId,String publishId,CommentType commentType,String content){
        try {
            //创建comment对象
            Comment comment=new Comment();
            //为comment对象赋值
            comment.setId(ObjectId.get());
            comment.setUserId(userId);
            //publishId是objectid类型的
            comment.setPublishId(new ObjectId(publishId));
            //评论类型
            comment.setCommentType(commentType.getType());
            //内容
            comment.setContent(content);
            comment.setCreated(System.currentTimeMillis());

            Publish publish=this.queryPublishById(publishId);
            if (ObjectUtil.isNotEmpty(publish)) {
                comment.setPublishUserId(publish.getUserId());
            } else {
                //查询评论
                Comment myComment = this.queryCommentById(publishId);
                if(ObjectUtil.isNotEmpty(myComment)){
                    comment.setPublishUserId(myComment.getUserId());
                }else{
                    //TODO 其他情况，比如小视频等
                    //查询小视频
                    //System.out.println("*************");
                    Video video=videoApi.queryVideoById(publishId);
                    //System.out.println("这里是公用的savecomment方法");
                    //System.out.println(video);
                    if(ObjectUtil.isNotEmpty(video)){
                        comment.setPublishUserId(video.getUserId());
                    }else {
                        //其他情况,直接返回
                        return false;
                    }
                }
            }
            mongoTemplate.save(comment);
            return true;
        } catch (Exception e) {
            log.error("保存Comment出错 userId="+userId+",publishId="+publishId+",commentType="+commentType,e);
        }
        return false;
    }

    /**
     * 根据id查询Comment对象
     * @param id
     * @return
     */
    private Comment queryCommentById(String id){
        return this.mongoTemplate.findById(new ObjectId(id),Comment.class);
    }

    /**
     * 根据id在publish表中查询发布的动态
     * @param id 动态id
     * @return
     */
    @Override
    public Publish queryPublishById(String id) {
        return this.mongoTemplate.findById(new ObjectId(id),Publish.class);
    }


    /**
     * 查询点赞数
     * @param publishId
     * @return
     */
    @Override
    public Long queryLikeCount(String publishId) {
        //先从redis中命中查询，如果命中直接返回即可
        String redisKey=this.getCommentRedisKeyPrefix(publishId);
        String hashKey=CommentType.LIKE.toString();
        Object data = redisTemplate.opsForHash().get(redisKey, hashKey);
        //判断查询的data是否为空
        if(ObjectUtil.isNotEmpty(data)){
            return Convert.toLong(data);
        }

        //redis没有则去MongoDB中查询,得到统计数量
        Long count=this.queryCommentCount(publishId,CommentType.LIKE);
        //写入redis中
        redisTemplate.opsForHash().put(redisKey,hashKey,String.valueOf(count));

        return count;
    }

    //查询MongoDB中quanzi_comment表，得到查询的数量
    private Long queryCommentCount(String publishId,CommentType commentType){
        Query query=Query.query(Criteria.where("publishId").is(new ObjectId(publishId))
                .and("commentType").is(commentType.getType()));
        return mongoTemplate.count(query,Comment.class);
    }

    /**
     * 取消点赞
     * @param userId
     * @param publishId
     * @return
     */
    @Override
    public Boolean disLikeComment(Long userId, String publishId) {
        //System.out.println(userId);
        //System.out.println(publishId);
        //判断用户是否已经点赞,如果没有点赞就返回
        if (!this.queryUserIsLike(userId,publishId)){
            //System.out.println("1111");
            return false;
        }


        //删除点赞数据
        Boolean result=this.removeComment(userId,publishId,CommentType.LIKE);
        //System.out.println(result);
        if(!result){
            //System.out.println("222");
            return false;
        }

        //修改点赞数
        String redisKey=this.getCommentRedisKeyPrefix(publishId);
        String hashKey=CommentType.LIKE.toString();
        redisTemplate.opsForHash().increment(redisKey,hashKey,-1);

        //将该用户的点赞删除（因为点赞由于是hash表存储，每个人的点赞都是会单独保存的，所以要删除掉）
        String userHashKey=this.getCommentUserLikeRedisKeyPrefix(userId);
        redisTemplate.opsForHash().delete(redisKey,userHashKey);

        return true;
    }

    /**
     * 删除MongoDB中的comment表相关数据
     * @param userId
     * @param publishId
     * @param commentType
     * @return
     */
    private Boolean removeComment(Long userId, String publishId, CommentType commentType) {
        //构造删除条件
        Query query=Query.query(Criteria.where("userId").is(userId)
                .and("publishId").is(new ObjectId(publishId))
                .and("commentType").is(commentType.getType()));


//        List<Comment> comments = mongoTemplate.find(query, Comment.class);
//        comments.forEach(comment -> System.out.println(comment));
//        System.out.println("*****");
//        System.out.println(mongoTemplate.remove(query,Comment.class).getDeletedCount());
//        System.out.println("******");
        return mongoTemplate.remove(query,Comment.class).getDeletedCount()>0;
    }

    /**
     * 圈子的喜欢
     * @param userId
     * @param publishId
     * @return
     */
    @Override
    public Boolean loveComment(Long userId, String publishId) {
        //查询用户是否已经喜欢
        if(this.queryUserIsLove(userId,publishId)){
            //System.out.println("已经喜欢");
            return false;
        }
        //喜欢
        boolean result=this.saveComment(userId,publishId,CommentType.LOVE,null);
        if(!result){
            return false;
        }
        //喜欢成功后,修改Redis中的总的喜欢数
        String redisKey=this.getCommentRedisKeyPrefix(publishId);
        String hashKey=CommentType.LOVE.toString();
        redisTemplate.opsForHash().increment(redisKey,hashKey,1);
        //标记用户已经喜欢
        hashKey=this.getCommentUserLoveRedisKey(userId);
        redisTemplate.opsForHash().put(redisKey,hashKey,"1");
        return true;
    }


    /**
     * 圈子的喜欢数
     * @param publishId
     * @return
     */
    @Override
    public Long queryLoveCount(String publishId) {
        //首先从redis中命中,如果命中的话就返回,没有命中就查询MongoDB
        String redisKey=this.getCommentRedisKeyPrefix(publishId);
        String hashKey=CommentType.LOVE.toString();
        Object value=this.redisTemplate.opsForHash().get(redisKey,hashKey);
        if(ObjectUtil.isNotEmpty(value)){
            return Convert.toLong(value);
        }
        //查询count
        Long count=this.queryCommentCount(publishId,CommentType.LOVE);

        //存储到redis中
        this.redisTemplate.opsForHash().put(redisKey,hashKey,String.valueOf(count));

        return null;
    }

    /**
     * 圈子的不喜欢数
     * @param userId
     * @param publishId
     * @return
     */
    @Override
    public Boolean disLoveComment(Long userId, String publishId) {
        if(!this.queryUserIsLove(userId,publishId)){
            //如果用户没有喜欢,就直接返回
            return false;
        }
        boolean result=this.removeComment(userId,publishId,CommentType.LOVE);
        if(!result){
            //删除失败
            return false;
        }
        //删除redis中的记录
        String redisKey=this.getCommentRedisKeyPrefix(publishId);
        String hashKey=this.getCommentUserLoveRedisKey(userId);
        this.redisTemplate.opsForHash().delete(redisKey,hashKey);
        this.redisTemplate.opsForHash().increment(redisKey,CommentType.LOVE.toString(),-1);

        return true;
    }

    /**
     * 查询是否喜欢
     * @param userId
     * @param publishId
     * @return
     */
    @Override
    public Boolean queryUserIsLove(Long userId, String publishId) {
        //设置redis中的key
        String redisKey=this.getCommentRedisKeyPrefix(publishId);
        //设置hashKey
        String hashKey=this.getCommentUserLoveRedisKey(userId);
        //根据redis的key以及hashkey获取value
        Object value=this.redisTemplate.opsForHash().get(redisKey,hashKey);
        //System.out.println("value="+value);
        //判断是否为空
        if(ObjectUtil.isNotEmpty(value)){
            return StrUtil.equals(Convert.toStr(value),"1");
        }

        //查询mongodb
        Query query=Query.query(Criteria.where("publishId")
                .is(new ObjectId(publishId))
                .and("userId").is(userId)
                .and("commentType").is(CommentType.LOVE.getType()));
        long count=this.mongoTemplate.count(query,Comment.class);
        //System.out.println("***");
        //System.out.println(count);
        if(count==0){
            return false;
        }
        //标记用户已经喜欢
        this.redisTemplate.opsForHash().put(redisKey,hashKey,"1");
        return true;
    }


    /**
     * 查询评论列表
     *
     * @param publishId
     * @param page
     * @param pageSize
     * @return
     */
    @Override
    public PageInfo<Comment> queryCommentList(String publishId, Integer page, Integer pageSize) {
        //构造分页
        PageRequest pageRequest = PageRequest.of(page - 1, pageSize, Sort.by(Sort.Order.asc("created")));
        //构造查询条件
        Query query = new Query(Criteria
                .where("publishId").is(new ObjectId(publishId))
                .and("commentType").is(CommentType.COMMENT.getType())).with(pageRequest);

        //查询评论列表
        List<Comment> commentList = this.mongoTemplate.find(query, Comment.class);

        PageInfo<Comment> pageInfo = new PageInfo<>();
        pageInfo.setPageNum(page);
        pageInfo.setPageSize(pageSize);
        pageInfo.setRecords(commentList);
        return pageInfo;
    }

    /**
     * 发表评论
     *
     * @param userId
     * @param publishId
     * @param content
     * @return
     */
    @Override
    public Boolean saveComment(Long userId, String publishId, String content) {
        return this.saveComment(userId, publishId, CommentType.COMMENT, content);
    }

    /**
     * 查询评论数
     * @param publishId
     * @return
     */
    @Override
    public Long queryCommentCount(String publishId) {
        return this.queryCommentCount(publishId, CommentType.COMMENT);
    }

    /**
     * 查询对我的点赞消息列表
     * @param userId
     * @param page
     * @param pageSize
     * @return
     */
    @Override
    public PageInfo<Comment> queryLikeCommentListByUser(Long userId, Integer page, Integer pageSize) {
        return this.queryCommentListByUser(userId, CommentType.LIKE, page, pageSize);
    }

    /**
     * 查询对我的喜欢消息列表
     * @param userId
     * @param page
     * @param pageSize
     * @return
     */
    @Override
    public PageInfo<Comment> queryLoveCommentListByUser(Long userId, Integer page, Integer pageSize) {
        return this.queryCommentListByUser(userId, CommentType.LOVE, page, pageSize);
    }

    /**
     * 查询对我的评论消息列表
     * @param userId
     * @param page
     * @param pageSize
     * @return
     */
    @Override
    public PageInfo<Comment> queryCommentListByUser(Long userId, Integer page, Integer pageSize) {
        return this.queryCommentListByUser(userId, CommentType.COMMENT, page, pageSize);
    }
    //抽取我的点赞、喜欢、评论消息列表公用的方法
    private PageInfo<Comment> queryCommentListByUser(Long userId, CommentType commentType, Integer page, Integer pageSize) {
        //构造分页查询的条件
        PageRequest pageRequest = PageRequest.of(page - 1, pageSize,
                Sort.by(Sort.Order.desc("created")));
        //构造查询条件
        Query query = new Query(Criteria
                .where("publishUserId").is(userId)
                .and("commentType").is(commentType.getType())).with(pageRequest);
        //去MongoDB的comment表里面进行查询
        List<Comment> commentList = this.mongoTemplate.find(query, Comment.class);
        //构造返回的视图对象
        PageInfo<Comment> pageInfo = new PageInfo<>();
        pageInfo.setPageNum(page);
        pageInfo.setPageSize(pageSize);
        pageInfo.setRecords(commentList);
        return pageInfo;
    }

    /**
     * 查看个人相册，这里也就是佳人自己发布的动态列表
     * @param userId
     * @param page
     * @param pageSize
     * @return
     */
    @Override
    public PageInfo<Publish> queryAlbumList(Long userId, Integer page, Integer pageSize) {
        //创建PageInfo对象
        PageInfo<Publish> pageInfo = new PageInfo<>();
        pageInfo.setPageNum(page);
        pageInfo.setPageSize(pageSize);
        //构造分页对象
        PageRequest pageRequest = PageRequest.of(page - 1 , pageSize,
                Sort.by(Sort.Order.desc("created")));
        //构造查询条件
        Query query = new Query().with(pageRequest);

        //查询自己的相册表
        List<Album> albumList = this.mongoTemplate.find(query, Album.class, "quanzi_album_" + userId);

        if(CollUtil.isEmpty(albumList)){
            return pageInfo;
        }
        //得到发布动态的publishId的集合
        List<Object> publishIdList = CollUtil.getFieldValues(albumList, "publishId");
        //构造查询条件
        Query queryPublish = Query.query(Criteria.where("id").in(publishIdList))
                .with(Sort.by(Sort.Order.desc("created")));
        //去Publish查询相关信息
        List<Publish> publishList = this.mongoTemplate.find(queryPublish, Publish.class);
        //完善pageInfo信息
        pageInfo.setRecords(publishList);

        return pageInfo;
    }

    @Override
    public Long queryCommentCount(String publishId, Integer type) {
        Query query = Query.query(Criteria.where("publishId").is(new ObjectId(publishId))
                .and("commentType").is(type));
        return this.mongoTemplate.count(query, Comment.class);
    }
}
